package itsm.isperp.framework.data.mybatis.dao;

import itsm.isperp.framework.core.context.ContextHolder;
import itsm.isperp.framework.data.dialect.DialectFactory;
import itsm.isperp.framework.data.domain.QueryFilter;
import itsm.isperp.framework.data.mybatis.utils.CUDMapperObjectUtils;
import itsm.isperp.framework.domain.IBean;
import itsm.isperp.framework.domain.IUser;
import itsm.isperp.framework.domain.TempOperator;
import itsm.isperp.framework.sequence.SequenceUtils;
import itsm.isperp.framework.utils.BeanUtils;
import itsm.isperp.framework.utils.Pair;

import java.util.Map;
import java.util.Set;

import javax.persistence.Table;

import org.apache.ibatis.jdbc.SQL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 增删改的sql模板
 * 
 * @author lizx
 * @date 2013-2-15
 * @version V1.0
 */
public class CUDTemplate<T extends IBean> {

	Logger logger = LoggerFactory.getLogger(CUDTemplate.class);

	private DataMethodAction<T> dataMethodAction;

	public interface DataMethodAction<T extends IBean> {

		/**
		 * 
		 * @param entity
		 * @param tableName
		 * @param includeColumns
		 */
		public void beforeSave(T entity, String tableName,
				Set<String> includeColumns);

		/**
		 * 
		 * @param entity
		 * @param tableName
		 * @param includeColumns
		 */
		public void beforeUpdate(T entity, String tableName,
				Set<String> includeColumns);

		/**
		 * 
		 * @param id
		 * @param tableName
		 */
		public boolean beforeDelete(String id, String tableName);
	}

	public CUDTemplate() {
		dataMethodAction = new DataMethodAction<T>() {

			IUser u = ContextHolder.getLoginUser();
			String userName, userFullmame;

			public void setUser(T entity) {
				// 执行事件修改日志
				if (u != null) {
					userName = u.getUsername();
					userFullmame = u.getFullname();
				} else {
					TempOperator op = ContextHolder.getTempOperator(entity
							.getId());
					if (op == null) {
						userName = userFullmame = "admin";
					} else {
						userName = u.getUsername();
						userFullmame = u.getFullname();
					}
				}
			}

			@Override
			public void beforeSave(T entity, String tableName,
					Set<String> includeColumns) {

				SequenceUtils.genericSequence(entity, includeColumns);
				BeanUtils.autoSetEntityInfo(entity, includeColumns, 0);
				setUser(entity);
				try {
					BeanUtils.doDatalog(entity, tableName, includeColumns, 0,
							userName, userFullmame);
				} catch (Exception e) {
					logger.error(e.getMessage(), e);
				} finally {
					ContextHolder.removeTempOperator(entity.getId());
				}
			}

			@Override
			public void beforeUpdate(T entity, String tableName,
					Set<String> includeColumns) {

				BeanUtils.autoSetEntityInfo(entity, includeColumns, 1);
				setUser(entity);
				// 执行事件修改日志
				try {
					BeanUtils.doDatalog(entity, tableName, includeColumns, 1,
							userName, userFullmame);
				} catch (Exception e) {
					logger.error(e.getMessage(), e);
				} finally {
					ContextHolder.removeTempOperator(entity.getId());
				}

			}

			@Override
			public boolean beforeDelete(String id, String tableName) {

				// 关联数据删除
				// 根据tableName获取所有关联外交，判断是否存在外键的值，如果存在，则禁止删除，否则执行删除操作

				BeanUtils.doDatalog(id, tableName, userName, userFullmame);

				return true;
			}

		};
	}

	public String saveMap(Map<String, Object> parameters) {
		@SuppressWarnings("unchecked")
		Map<String, Object> m = (Map<String, Object>) parameters.get("param1");

		String tableName = (String) parameters.get("param2");

		SQL sql = new SQL();
		sql.INSERT_INTO(tableName);

		StringBuilder columns = new StringBuilder();
		StringBuilder values = new StringBuilder();

		int i = 0;
		for (String key : m.keySet()) {
			if (i != 0) {
				columns.append(",");
				values.append(",");

			}
			i++;
			columns.append(key);
			values.append("#{").append(key).append("}");

		}

		sql.VALUES(columns.toString(), values.toString());

		return sql.toString();
	}

	/**
	 * 获取新增实体的sql语句
	 * 
	 * @param entity
	 *            实体
	 * @return sql语句
	 */
	public <S extends T> String save(S entity) {
		// @SuppressWarnings("unchecked")
		// T entity = (T) parameters.get("param1");
		Class<? extends IBean> clazz = entity.getClass();

		Table table = clazz.getAnnotation(Table.class);
		if (table == null)
			return null;

		// SequenceUtils.genericSequence(entity);
		// BeanUtils.autoSetEntityInfo(entity);

		dataMethodAction.beforeSave(entity, table.name(), null);

		SQL sql = new SQL();
		sql.INSERT_INTO(table.name());
		sql.VALUES(CUDMapperObjectUtils.getInsertColumnsName(clazz),
				CUDMapperObjectUtils.getInsertColumnsDefine(entity)
						.getRightObject());

		return sql.toString();

	}

	public String dynamicSpecificSave(Map<String, Object> parameters)
			throws SecurityException, NoSuchFieldException,
			IllegalArgumentException, IllegalAccessException {
		@SuppressWarnings("unchecked")
		T entity = (T) parameters.get("param1");
		@SuppressWarnings("unchecked")
		Set<String> includeColumns = (Set<String>) parameters.get("param2");

		Class<? extends IBean> clazz = entity.getClass();

		Table table = clazz.getAnnotation(Table.class);
		if (table == null)
			return null;
		SQL sql = new SQL();
		sql.INSERT_INTO(table.name());

		// SequenceUtils.genericSequence(entity, includeColumns);
		// BeanUtils.autoSetEntityInfo(entity, includeColumns);

		dataMethodAction.beforeSave(entity, table.name(), includeColumns);

		Pair<String, String> cvs = CUDMapperObjectUtils
				.getDynamicInsertSetFromSpecificColumns(entity, includeColumns);
		sql.VALUES(cvs.getLeftObject(), cvs.getRightObject());

		return sql.toString();

	}

	/**
	 * 获取实体的更新语句
	 * 
	 * @param entity
	 *            实体
	 * @return 更新语句
	 */
	public <S extends T> String update(Object parameters) {
		String prefix = "";
		T entity = null;
		Class<? extends IBean> clazz = null;
		if (parameters instanceof IBean) {
			entity = ((T) parameters);

			clazz = ((T) entity).getClass();
		} else {
			@SuppressWarnings("unchecked")
			Map<String, Object> p = (Map<String, Object>) parameters;
			prefix = "param1.";
			entity = (T) p.get("param1");
			clazz = entity.getClass();
		}

		String idname = CUDMapperObjectUtils.getPrimaryKeyName(clazz);
		Table table = clazz.getAnnotation(Table.class);
		if (table == null)
			return null;
		SQL sql = new SQL();
		sql.UPDATE(table.name());

		dataMethodAction.beforeUpdate(entity, table.name(), null);

		sql.SET(CUDMapperObjectUtils.getUpdateSet(clazz, prefix));
		sql.WHERE(idname + "=#{" + prefix + idname + "}");
		return sql.toString();
	}

	/**
	 * 获取删除语句
	 * 
	 * @param parameters
	 * @return
	 */
	public String existsByClass(Map<String, Object> parameters) {
		@SuppressWarnings("unchecked")
		Class<? extends IBean> clazz = (Class<? extends IBean>) parameters
				.get("clazz");
		String id = (String) parameters.get("id");
		String idname = CUDMapperObjectUtils.getPrimaryKeyName(clazz);
		Table table = clazz.getAnnotation(Table.class);
		if (table == null)
			return null;
		SQL sql = new SQL();
		sql.SELECT("1");
		sql.FROM(table.name());
		sql.WHERE(idname + "='" + id + "'");

		return DialectFactory.getDialect().getLimitString(sql.toString());
	}

	/**
	 * 获取删除语句
	 * 
	 * @param parameters
	 * @return
	 */
	public String deleteByClass(Map<String, Object> parameters) {
		@SuppressWarnings("unchecked")
		Class<? extends IBean> clazz = (Class<? extends IBean>) parameters
				.get("clazz");
		String id = (String) parameters.get("id");
		String idname = CUDMapperObjectUtils.getPrimaryKeyName(clazz);

		Table table = clazz.getAnnotation(Table.class);
		if (table == null)
			return null;

		dataMethodAction.beforeDelete(id, table.name());

		SQL sql = new SQL();

		sql.DELETE_FROM(table.name());
		sql.WHERE(idname + "='" + id + "'");

		return sql.toString();
	}

	public String deleteAll(Map<String, Object> parameters) {
		@SuppressWarnings("unchecked")
		Class<? extends IBean> clazz = (Class<? extends IBean>) parameters
				.get("clazz");

		Table table = clazz.getAnnotation(Table.class);
		if (table == null)
			return null;

		SQL sql = new SQL();

		sql.DELETE_FROM(table.name());

		return sql.toString();
	}

	/**
	 * 获取删除语句
	 * 
	 * @param parameters
	 * @return
	 */
	public String findOneByClass(Map<String, Object> parameters) {
		@SuppressWarnings("unchecked")
		Class<? extends IBean> clazz = (Class<? extends IBean>) parameters
				.get("clazz");
		String id = (String) parameters.get("id");
		String idname = CUDMapperObjectUtils.getPrimaryKeyName(clazz);

		Table table = clazz.getAnnotation(Table.class);
		if (table == null)
			return null;
		SQL sql = new SQL();

		sql.SELECT("*");
		sql.FROM(table.name());
		sql.WHERE(idname + "='" + id + "'");

		return sql.toString();
	}

	public String findOneByClassAndRequest(Map<String, Object> parameters) {
		@SuppressWarnings("unchecked")
		Class<? extends IBean> clazz = (Class<? extends IBean>) parameters
				.get("param2");
		String id = (String) parameters.get("param1");

		String idname = CUDMapperObjectUtils.getPrimaryKeyName(clazz);

		Table table = clazz.getAnnotation(Table.class);
		if (table == null)
			return null;

		return " FROM " + table.name() + " WHERE " + idname + "='" + id + "'";
	}

	public String findOneByClassAndColumns(Map<String, Object> parameters) {
		@SuppressWarnings("unchecked")
		Class<? extends IBean> clazz = (Class<? extends IBean>) parameters
				.get("param2");
		String id = (String) parameters.get("param1");
		String[] selectColumns = (String[]) parameters.get("param3");

		String idname = CUDMapperObjectUtils.getPrimaryKeyName(clazz);

		Table table = clazz.getAnnotation(Table.class);
		if (table == null)
			return null;
		SQL sql = new SQL();

		if (selectColumns == null || selectColumns.length == 0)
			sql.SELECT("*");
		else {
			StringBuilder sb = new StringBuilder(
					itsm.isperp.framework.utils.StringUtils
							.underscoreForSql(selectColumns[0]));

			for (int i = 1; i < selectColumns.length; i++) {
				sb.append(",").append(
						itsm.isperp.framework.utils.StringUtils
								.underscoreForSql(selectColumns[i]));
			}
			sql.SELECT(sb.toString());
		}
		sql.FROM(table.name());
		sql.WHERE(idname + "='" + id + "'");

		return sql.toString();

	}

	public String findAllByClass(Map<String, Object> parameters) {
		@SuppressWarnings("unchecked")
		Class<? extends IBean> clazz = (Class<? extends IBean>) parameters
				.get("clazz");

		Table table = clazz.getAnnotation(Table.class);
		if (table == null)
			return null;
		SQL sql = new SQL();

		sql.SELECT("*");
		sql.FROM(table.name());

		return sql.toString();
	}

	// /**
	// * 获取动态更新语句
	// *
	// * @param entity
	// * 实体
	// * @return 更新语句
	// * @throws IllegalAccessException
	// * @throws IllegalArgumentException
	// * @throws NoSuchFieldException
	// * @throws SecurityException
	// */
	// public String dynamicUpdate(T entity) throws SecurityException,
	// NoSuchFieldException, IllegalArgumentException,
	// IllegalAccessException {
	// SQL sql = new SQL();
	//
	// Class<? extends IBean> clazz = entity.getClass();
	//
	// String idname = CUDMapperObjectUtils.getPrimaryKeyName(clazz);
	// Table table = clazz.getAnnotation(Table.class);
	// if (table == null)
	// return null;
	//
	// // SequenceUtils.genericSequence(entity);
	// // BeanUtils.autoSetEntityInfo(entity);
	// //
	// dataMethodAction.beforeUpdate(entity, null);
	//
	// sql.UPDATE(table.name());
	// sql.SET(CUDMapperObjectUtils.getDynamicUpdateSet(entity, null));
	// sql.WHERE(idname + "=#{" + idname + "}");
	// return sql.toString();
	// }
	//
	// public String dynamicUpdateEntity(Map<String, Object> parameters)
	// throws SecurityException, NoSuchFieldException,
	// IllegalArgumentException, IllegalAccessException {
	// SQL sql = new SQL();
	//
	// @SuppressWarnings("unchecked")
	// T entity = (T) parameters.get("param1");
	// @SuppressWarnings("unchecked")
	// T oldEntity = (T) parameters.get("param2");
	//
	// Class<? extends IBean> clazz = entity.getClass();
	//
	// String idname = CUDMapperObjectUtils.getPrimaryKeyName(clazz);
	// Table table = clazz.getAnnotation(Table.class);
	// if (table == null)
	// return null;
	// sql.UPDATE(table.name());
	//
	// BeanUtils.autoSetEntityInfo(entity);
	//
	// String set = CUDMapperObjectUtils.getDynamicUpdateSetBetweenTowEntity(
	// entity, oldEntity);
	// if (set == null) {
	// return "SELECT 1 FROM " + table.name() + " LIMIT 1";
	// }
	//
	// sql.SET(set);
	// sql.WHERE(idname + "=#{param1." + idname + "}");
	// return sql.toString();
	// }

	public String dynamicSpecificUpdate(Map<String, Object> parameters)
			throws SecurityException, NoSuchFieldException,
			IllegalArgumentException, IllegalAccessException {
		@SuppressWarnings("unchecked")
		T entity = (T) parameters.get("param1");
		@SuppressWarnings("unchecked")
		Set<String> includeColumns = (Set<String>) parameters.get("param2");
		SQL sql = new SQL();

		Class<? extends IBean> clazz = entity.getClass();

		String idname = CUDMapperObjectUtils.getPrimaryKeyName(clazz);
		Table table = clazz.getAnnotation(Table.class);
		if (table == null)
			return null;
		sql.UPDATE(table.name());

		// BeanUtils.autoSetEntityInfo(entity, includeColumns);
		dataMethodAction.beforeUpdate(entity, table.name(), includeColumns);
		String set = CUDMapperObjectUtils
				.getDynamicUpdateSetFromSpecificColumns(entity, includeColumns,
						true);
		if (set == null) {
			return "SELECT 'X'";
		}

		sql.SET(set);
		sql.WHERE(idname + "=#{param1." + idname + "}");
		return sql.toString();

	}

	public String dynamicButSpecificUpdate(Map<String, Object> parameters)
			throws SecurityException, NoSuchFieldException,
			IllegalArgumentException, IllegalAccessException {
		@SuppressWarnings("unchecked")
		T entity = (T) parameters.get("param1");
		@SuppressWarnings("unchecked")
		Set<String> unIncludeColumns = (Set<String>) parameters.get("param2");
		SQL sql = new SQL();

		Class<? extends IBean> clazz = entity.getClass();

		String idname = CUDMapperObjectUtils.getPrimaryKeyName(clazz);
		Table table = clazz.getAnnotation(Table.class);
		if (table == null)
			return null;
		sql.UPDATE(table.name());

		// BeanUtils.autoSetEntityInfo(entity);
		dataMethodAction.beforeUpdate(entity, table.name(), null);

		String set = CUDMapperObjectUtils
				.getDynamicUpdateSetFromSpecificColumns(entity, unIncludeColumns,
						false);
		if (set == null) {
			return "SELECT 'X'";
		}

		sql.SET(set);
		sql.WHERE(idname + "=#{param1." + idname + "}");
		return sql.toString();

	}

	public String findAllCount(Map<String, Object> parameters) {

		Class<?> clazz = (Class<?>) parameters.get("param1");
		Table table = clazz.getAnnotation(Table.class);

		QueryFilter filter = (QueryFilter) parameters.get("param2");
		SQL sql = new SQL();

		sql.SELECT("count(1) count_all");
		sql.FROM(table.name());
		if (filter != null) {
			sql.WHERE(" 1=1 " + filter.getSql());
		}
		return sql.toString();

	}
}
